package com.lagou.edu.ioc;

import com.lagou.edu.ioc.support.BeanDefinition;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by leon.L on 2020/1/7.
 * Desc : 定义实现容器的基本功能以及部分初始化模版方法
 */
public abstract class AbstractApplicationContext implements ApplicationContext {

    /** 缓存 bean 的单例且非懒加载实例：bean name to bean instance。 */
    protected final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** 缓存类型关联的单例实例：bean class to bean instance */
    protected final Map<Class<?>, Object> singletonObjectsClassMap = new ConcurrentHashMap<>(256);

    /** 缓存未完全实例化的 bean 对象：bean name to bean instance。 */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    /** 创建 Bean 实例过程中需要应用的 BeanPostProcessors  （org.springframework.beans.factory.support.AbstractBeanFactory） */
    private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

    /** 缓存需要被实例的 bean 描述信息 */
    protected final Map<String, BeanDefinition> beanDefinitionRegistry = new HashMap<>(16);

    /** 缓存初始化过程中已经被实例的 bean 的描述信息 */
    protected final Set<String> registeredBeanDefinition = new HashSet<>(16);

    /** 缓存处理中的 bean 的描述信息 */
    protected final Set<String> registeringBeanDefinition = new HashSet<>(16);


    @Override
    public Object getBean(String name) {
        return singletonObjects.get(name);
    }

    @Override
    public Object getBean(Class<?> clazz) {
        for (Map.Entry<String, Object> entry : singletonObjects.entrySet()) {
            if (entry.getValue().getClass() == clazz) {
                return entry.getValue();
            }
        }
        for (Map.Entry<Class<?>, Object> entry : singletonObjectsClassMap.entrySet()) {
            if (entry.getKey() == clazz || Arrays.asList( entry.getKey().getInterfaces() ).contains(clazz)) {
                return entry.getValue();
            }
        }
        return null;
    }

    /**
     * 实例化 Bean
     * @param beanName bean 的名称
     * @param requiredType bean 的类型
     * @param <T> bean 的实例
     */
    protected <T> T doGetBean(final String beanName, final Class<T> requiredType) throws Exception {

        Object beanObj = singletonObjects.get(beanName);
        if (beanObj == null) {
            // 标记 bean 正在处理中
            registeringBeanDefinition.add(beanName);

            // 1 根据无参构造实例化bean
            Constructor<T> constructor = requiredType.getConstructor();
            beanObj = constructor.newInstance();

            // 2 把 bean 放到二级缓存中
            earlySingletonObjects.put(beanName, beanObj);

            // 3 处理实例属性依赖
            processDependentBean(beanName, beanObj);

            // 4 把当前bean从二级缓存移动到一级缓存中
            singletonObjects.put(beanName, beanObj);
            earlySingletonObjects.remove(beanName);
            singletonObjectsClassMap.put(requiredType, beanObj);

            // 如果当前 bean 实现了 BeanPostProcessor 接口，则放入到 beanPostProcessors 集合中
            if (Arrays.asList( requiredType.getInterfaces() ).contains(BeanPostProcessor.class)){
                beanPostProcessors.add((BeanPostProcessor) beanObj);
            }else{
                // bean 实例化完成，记录的已经注册完成记录中
                registeredBeanDefinition.add(beanName);
            }
            // 从处理中状态缓存中移除
            registeringBeanDefinition.remove(beanName);
        }

        return (T) beanObj;
    }

    /**
     * 处理实例属性依赖。
     * 判断是否有依赖，如果有，从一级缓存中找，找不到从 BeanDefinition 集合中找到 bean 定义信息，走实例化过程
     * @param beanName
     * @param beanObj
     * @throws NoSuchFieldException
     */
    protected void processDependentBean(String beanName, Object beanObj) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionRegistry.get(beanName);
        Map<String, Class<?>> dependencies = beanDefinition.getDependencies();

        for (Map.Entry<String, Class<?>> entry : dependencies.entrySet()) {

            boolean hasProperty = false;
            Object target = null;

            // 先从 singletonObjects 和 earlySingletonObjects 中找可用的依赖对象
            Set<Object> allObjects = new HashSet<>();
            allObjects.addAll( singletonObjects.values() );
            allObjects.addAll( earlySingletonObjects.values() );

            for (Object o : allObjects) {
                // 判断当前 bean 实例的当前依赖的类型或者其接口类型是否能在 singletonObjects 和 earlySingletonObjects 中找到
                List<Class<?>> cl = new ArrayList<>();
                traceSuperClass(o.getClass(), cl);
                if (o.getClass() == entry.getValue() || cl.contains(entry.getValue())) {
                    target = o;
                    hasProperty = true;
                    break ;
                }
            }

            // 没有合适的实例对象，在从 beanDefinitionRegistry 去找有没有尚未被实例的合适的 bean 信息，如果有就去创建，如果还没有就抛异常
            if (!hasProperty) {
                for (Map.Entry<String, BeanDefinition> se : beanDefinitionRegistry.entrySet()) {
                    String bName = se.getKey();
                    BeanDefinition bd = se.getValue();
                    Class<?> beanClass = Class.forName(bd.getBeanClass());

                    // 如果 bean 已经在处理过的或者仍在处理中，则不在处理
                    if (registeredBeanDefinition.contains(bName)) {
                        continue;
                    }
                    if (registeringBeanDefinition.contains(bName)) {
                        continue;
                    }

                    //  beanDefinition 的类和需要被依赖注入的属性一致或者是其子类，则去创建
                    if (beanClass == entry.getValue() || Arrays.asList(beanClass.getInterfaces()).contains(entry.getValue())){
                        target = doGetBean(bd.getInstanceName(), beanClass);
                        hasProperty = true;
                    }
                }
            }

            if (hasProperty){
                Field field = beanObj.getClass().getDeclaredField(entry.getKey());
                field.setAccessible(true);
                field.set(beanObj, target);
            } else {
                throw new RuntimeException("依赖属性注入失败，属性" + entry.getKey() + "未找到");
            }
        }
    }

    /**
     * 获取一个类的接口继承总集合
     * @param targetClass 需要检查继承树的目标类
     * @return
     */
    private void traceSuperClass(Class<?> targetClass, List<Class<?>> classList){
        Class<?>[] interfaces = targetClass.getInterfaces();
        if (interfaces.length > 0) {
            classList.addAll(Arrays.asList(interfaces));
            for (Class<?> anInterface : interfaces) {
                traceSuperClass(anInterface, classList);
            }
        }
    }

    /**
     * 实例化 bean
     */
    protected void initBean() throws Exception{
        for (Map.Entry<String, BeanDefinition> definitionEntry : beanDefinitionRegistry.entrySet()) {
            doGetBean(definitionEntry.getKey(), Class.forName(definitionEntry.getValue().getBeanClass()));
        }
    }

    /**
     * 加载类描述信息模版方法
     */
    protected abstract void registerBean(Class<?> clazz);

    /**
     * 应用 bean 的后置处理
     */
    protected void applyBeanPostProcessor() throws Exception {
        for (Map.Entry<String, Object> objectEntry : singletonObjects.entrySet()) {
            Object bean = objectEntry.getValue();
            String beanName = objectEntry.getKey();

            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                Object newBean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
                singletonObjects.put(beanName, newBean);
                singletonObjectsClassMap.put(bean.getClass(), newBean);
            }
        }
    }

    /**
     * 初始化容器
     */
    @Override
    public void refresh() {
        try {
            // 实例化 bean
            initBean();

            // 对所有 bean 应用后置处理器
            applyBeanPostProcessor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
